
<template>
  <a-select
    showSearch
    allowClear
    labelInValue
    :disabled="disabled"
    v-model="selectedValue"
    optionFilterProp="children"
    :mode="mode"
    :notFoundContent="loading ? undefined : null"
    :placeholder="placeholder"
    :getPopupContainer="getPopupContainer"
    @select="handleSelect"
    @search="handleSearch"
    @change="handleChange"
    @dropdownVisibleChange="dropdownVisibleChange"
  >
    <div slot="dropdownRender" slot-scope="menu">
      <a-spin :spinning="loading">
        <v-nodes :vnodes="menu" />
        <a-divider style="margin:4px 0" />
        <div @mousedown="e => e.preventDefault()">
          <!-- show-quick-jumper -->
          <a-pagination
            size="small"
            show-less-items
            style="margin-bottom:4px;margin-left:10px;"
            :show-total="total => `共 ${total} 条记录`"
            showLessItems
            v-bind="ipagination"
            @change="pageChange"
          />
        </div>
      </a-spin>
    </div>
    <a-select-option v-for="(item, index) in options" :key="index" :value="item.key">{{ item.label }}</a-select-option>
  </a-select>
</template>
<script>
import { Pagination as APagination } from 'ant-design-vue'
import { getAction } from '@/services/manage'
import Criteria from '@/utils/criteria'
import { OPERATOR } from '@/store/mutation-types'
import { filterObj, isEmpty } from '@/utils/util'
// import isNil from 'lodash/isNil'
import debounce from 'lodash/debounce'
export default {
  name: 'ZAsyncSelect',
  props: {
    value: [String, Number],
    placeholder: {
      type: String,
      default: '请选择'
    },
    url: String,
    optionKey: {
      type: Object,
      default: () => ({ label: 'label', value: 'value' })
    },
    filters: {
      type: Object,
      default: () => {}
    },
    disabled: Boolean,
    getPopupContainer: {
      type: Function,
      default: () => document.body
    },
    popupContainer: String, // 自定义属性，方便设置下拉框popupContainer属性 body|parent
    mode: { // multiple || default
      type: String
    }
  },
  components: {
    VNodes: {
      functional: true,
      render: (h, ctx) => ctx.props.vnodes
    },
    APagination
  },
  data() {
    return {
      selectedValue: '',
      ipagination: {
        total: 0,
        currentPage: 1,
        pageSize: 10
      },
      options: [],
      loading: false,
      queryParam: {},
      oriDataMap: new Map()
    }
  },
  watch: {
    'value': {
      immediate: true,
      handler(val) {
        this.setSelectValue(val)
      }
    },
    'url': {
      async handler(val) {
        await this.loadData()
      },
      immediate: true
    }
  },
  mounted() {
    this.selectedValue = this.mode === 'multiple' ? [] : ''
  },
  methods: {
    pageChange(page, pageSize) { // 翻页
      this.ipagination.currentPage = page
      this.ipagination.pageSize = pageSize
      this.loadData()
    },
    handleSearch: debounce(function(val) { // 搜索
      this.queryParam[this.optionKey.label] = val
      this.ipagination.currentPage = 1
      this.searchVal = val
      this.loadData()
    }, 400),
    handleSelect() {
      // this.queryParam[this.optionKey.label] = ''
      // this.ipagination.currentPage = 1
      // this.loadData()
    },
    async loadData() { // 请求列表
      if (!this.url) {
        this.options = []
        return
      }
      this.loading = true
      var params = this.getQueryParams()// 查询条件
      const res = await getAction(this.url, params)
      this.loading = false
      if (res.success) {
        this.setOptions(res.result.content || res.result)
        this.ipagination.total = res.result.totalElements
      } else {
        this.$message.warning(res.message)
      }
    },
    setOptions(list) { // 组装options
      const options = []
      // this.oriDataMap.clear()
      list.forEach(item => {
        const data = {
          label: item[this.optionKey.label],
          key: item[this.optionKey.value]
        }
        this.oriDataMap.set(data.key, item)
        options.push(data)
      })
      this.options = options
    },
    getQueryParams() {
      // 获取查询条件
      const queryParam = new Criteria()
      queryParam.addCriterions(Object.assign({}, filterObj(this.queryParam), this.filters), OPERATOR.LIKE)// 过滤参数转换为jpa支持格式
      var param = Object.assign({}, { criteria: queryParam.getEncode() })
      param.pageNo = this.ipagination.currentPage
      param.pageSize = this.ipagination.pageSize
      return filterObj(param)
    },
    // 组装select选中值
    async setSelectValue(val) {
      if (isEmpty(val)) {
        this.selectedValue = this.mode === 'multiple' ? [] : ''
        return
      }
      if (this.mode === 'multiple') { // 多选
        const list = val.split(',')
        const selectedObj = await this.getSelectValue(val)
        const selectedValue = []
        list.forEach(item => { // 按顺序展示
          selectedValue.push(selectedObj[item])
        })
        this.selectedValue = selectedValue
      } else { // 单选
        const selectedObj = await this.getSelectValue(val)
        this.selectedValue = selectedObj[val]
        // if (!this.selectedValue || this.selectedValue.key !== val || !isNil(this.selectedValue.label)) {
        //   const selectedObj = await this.getSelectValue(val)
        //   this.selectedValue = selectedObj[val]
        // }
      }
    },
    async getSelectValue(value) { // 根据value获取option
      const valueList = value.split(',')
      const options = {} // 缓存命中集合
      const asyncVals = [] // 缓存未命中值集合
      const asyncOptions = {} // 后台查询集合
      valueList.forEach(item => {
        const option = this.oriDataMap.get(item)// 先在缓存的map对象中获取
        option ? options[item] = {
          label: option[this.optionKey.label],
          key: option[this.optionKey.value]
        } : asyncVals.push(item)
      })
      if (asyncVals.length > 0) { // 缓存的map对象未命中，后台查询
        const param = {}
        param[`${this.optionKey.value}@IN`] = asyncVals.join(',')
        const res = await this.asyncGetSelectValue(param)
        if (res.success) {
          const list = res.result.content || res.result
          list.forEach(item => {
            const key = item[this.optionKey.value]
            this.oriDataMap.set(key, item)
            asyncOptions[key] = { label: item[this.optionKey.label], key: key }
          })
        }
      }
      return Object.assign({}, options, asyncOptions)
    },
    async asyncGetSelectValue(param_) { // 请求后台查询该选中数据
      const queryParam = new Criteria()
      queryParam.addCriterions(Object.assign({}, param_), OPERATOR.LIKE_START)// 过滤参数转换为jpa支持格式
      var param = Object.assign({}, { criteria: queryParam.getEncode() })
      param.pageNo = 1
      param.pageSize = 999
      const res = await getAction(this.url, param)
      return res
    },
    handleChange(selectedValue) {
      if (this.mode === 'multiple') {
        const value = []
        const selectOptions = []
        selectedValue.forEach(item => {
          value.push(item.key)
          selectOptions.push(this.oriDataMap.get(item.key) || { [this.optionKey.value]: item.key })
        })
        this.$emit('change', value.join(), selectOptions)
      } else {
        this.selectedValue = selectedValue
        const value = this.selectedValue?.key
        this.$emit('change', value, value ? this.oriDataMap.get(value) || { [this.optionKey.value]: value } : {})
      }
    },
    getPopupContainerFn(node) {
      if (this.popupContainer === 'body') {
        return document.body
      } else if (this.popupContainer === 'parent') {
        return node.parentNode
      }
      return this.getPopupContainer(node)
    },
    dropdownVisibleChange(open) { // 修复多选模式，未搜索到内容，无法再次展开下拉框
      if (this.mode === 'multiple' && this.searchVal && open) {
        this.handleSearch('')
      }
    }
  },
  model: {
    prop: 'value',
    event: 'change'
  }
}
</script>
